package cn.song.docflow.config;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * HTTP请求包装器类
 * 用于解决请求体只能读取一次的问题
 * <p>
 * 主要功能：
 * 1. 将请求体数据保存在内存中
 * 2. 支持请求体数据的多次读取
 * 3. 提供便捷的请求数据获取方法
 *
 * @author SongRenShuo
 * @date 2025/04/03
 */
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {

    /**
     * 用于存储请求体数据的字节数组
     * 每个请求都会创建新的实例，确保线程安全
     */
    private final byte[] body;

    /**
     * 构造函数
     * 初始化时会读取原始请求的数据并保存
     *
     * @param request 原始的HTTP请求对象
     */
    public RequestWrapper(HttpServletRequest request) {
        super(request);
        // 读取请求体数据并保存，明确使用UTF-8编码
        body = getBodyString(request).getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 获取请求体数据
     * 静态工具方法，方便直接获取请求体内容
     *
     * @param request 原始的HTTP请求对象
     * @return 请求体的字符串内容
     */
    public static String getRequestBody(HttpServletRequest request) {
        return new RequestWrapper(request).getBodyString();
    }

    /**
     * 获取表单数据
     * 将请求中的表单参数转换为Map格式
     *
     * @param request 原始的HTTP请求对象
     * @return 包含表单数据的Map对象
     */
    public static Map<String, String> getRequestFormData(HttpServletRequest request) {
        Map<String, String> formDataMap = new HashMap<>();
        request.getParameterMap().forEach((key, values) -> {
            if (values.length > 0) {
                formDataMap.put(key, values[0]);
            }
        });
        return formDataMap;
    }

    /**
     * 读取请求体数据
     * 从原始请求中读取数据并转换为字符串
     *
     * @param request Servlet请求对象
     * @return 请求体的字符串内容
     */
    private String getBodyString(final ServletRequest request) {
        try {
            return inputStream2String(request.getInputStream());
        } catch (IOException e) {
            log.error("读取请求体数据失败：{}", e.getMessage(), e);
            return "";
        }
    }

    /**
     * 获取保存的请求体数据
     * 将保存的字节数组转换为字符串
     *
     * @return 请求体的字符串内容
     */
    public String getBodyString() {
        final InputStream inputStream = new ByteArrayInputStream(body);
        return inputStream2String(inputStream);
    }

    /**
     * 将输入流转换为字符串
     * 读取输入流中的所有数据并拼接成字符串
     *
     * @param inputStream 输入流对象
     * @return 输入流中的数据转换后的字符串
     */
    private String inputStream2String(InputStream inputStream) {
        StringBuilder sb = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error("读取输入流数据失败：{}", e.getMessage(), e);
        }
        return sb.toString();
    }

    /**
     * 重写获取Reader的方法
     * 返回一个可以重复读取的BufferedReader对象
     *
     * @return BufferedReader对象
     */
    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 重写获取InputStream的方法
     * 返回一个可以重复读取的ServletInputStream对象
     *
     * @return ServletInputStream对象
     */
    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public int read() {
                return inputStream.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
                // 暂未实现异步读取监听器
            }
        };
    }
}
